package test;

import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.OneShotBehaviour;
import jade.domain.FIPANames;
import jade.lang.acl.ACLMessage;

import java.util.logging.Level;

import de.fhin.mas.MASEvents;
import de.fhin.mas.ontology.EnvironmentData;
import de.fhin.mas.ontology.EnvironmentScanResult;
import de.fhin.mas.ontology.Location;
import de.fhin.mas.ontology.SearchableObject;
import de.fhin.mas.ontology.SearchableObjectData;
import de.fhin.mas.ontology.TargetData;
import de.fhin.mas.ontology.TargetSearchResult;
import de.fhin.mas.util.ArgumentCheck;

/**
 * Diese abstrakte Basisklasse fuer Tests stellt grundlegende Funktionalitaeten
 * zur Verfuegung.
 * 
 * @author Alexander Weickmann
 * @author Thomas Schlaefer
 */
public abstract class BaseCommunicationTestBehaviour extends OneShotBehaviour {

	/** Die eindeutige ID zur Serialisierung ueber das Netzwerk. */
	private static final long serialVersionUID = -26567597999409488L;

	/**
	 * Die Zeit, welche normalerweise auf erwartete Messages höchstens gewartet
	 * wird.
	 */
	protected static final int DEFAULT_RESPONSE_TIME = 4000;

	/**
	 * Erzeugt das <tt>BasicSearcherTestBehaviour</tt>.
	 * 
	 * @param parent
	 *            Der Test-Agent, zu dem das <tt>TestBehaviour</tt> gehoert.
	 */
	protected BaseCommunicationTestBehaviour(Agent parent) {
		super(parent);
		ArgumentCheck.notNull(parent);
	}

	/**
	 * Erzeugt eine <tt>ACLMessage</tt> mit den angegebenen Parametern und
	 * versendet diese.
	 * 
	 * @param protocol
	 *            Das zu verwendende Protokoll.
	 * @param conversationId
	 *            Die zu verwendende Conversation-ID.
	 * @param performative
	 *            Das zu verwendende Performative.
	 * @param receivers
	 *            Die Empfaenger der Nachricht.
	 * 
	 * @throws IllegalArgumentException
	 *             Falls <tt>receivers</tt> nicht mindestens einen Eintrag hat.
	 * @throws NullPointerException
	 *             Falls <tt>receivers</tt> <tt>null</tt> ist.
	 */
	protected void buildAndSendMessage(String protocol, String conversationId, int performative, AID[] receivers) {

		ArgumentCheck.notNull(receivers);
		ArgumentCheck.isTrue(receivers.length > 0);

		ACLMessage message = new ACLMessage(performative);
		for (AID currentReceiver : receivers) {
			message.addReceiver(currentReceiver);
		}
		message.setConversationId(conversationId);
		message.setProtocol(protocol);
		myAgent.send(message);
	}

	/**
	 * Erzeugt eine NEW_ORDER CFP <tt>ACLMessage</tt> mit dem zu suchenden
	 * Objekt im Content und versendet sie an die angegebenen Empfänger.
	 * 
	 * @param receivers
	 *            Die Empfaenger der Nachricht.
	 * 
	 * @param target
	 *            Das zu suchende <tt>SearchableObject</tt>
	 * @param location
	 *            Der Lagerort für das zu suchende Objekt
	 * 
	 * @throws IllegalArgumentException
	 *             Falls <tt>receivers</tt> nicht mindestens einen Eintrag hat.
	 * @throws NullPointerException
	 *             Falls <tt>receivers</tt> <tt>null</tt> ist.
	 */
	protected void buildAndSendNewOrderCFPMessage(AID[] receivers, SearchableObject target, Location location) {

		ArgumentCheck.notNull(receivers);
		ArgumentCheck.isTrue(receivers.length > 0);

		ACLMessage message = new ACLMessage(ACLMessage.CFP);
		for (AID currentReceiver : receivers) {
			message.addReceiver(currentReceiver);
		}

		message.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
		message.setConversationId(MASEvents.NEW_ORDER.getName());

		// *************************************
		// ontologie relevantes zeuch
		BaseTestAgent bta = (BaseTestAgent) myAgent;
		// Content Language und Ontologie der Message setzen:
		message.setLanguage(bta.getCodec().getName());
		message.setOntology(bta.getOntology().getName());

		SearchableObjectData data = new SearchableObjectData(target, location);

		try {
			// JADE konvertiert das SearchableObjectData JavaObjekt in einen SL
			// language String:
			myAgent.getContentManager().fillContent(message, data);
		} catch (CodecException ce) {
			ce.printStackTrace();
		} catch (OntologyException oe) {
			oe.printStackTrace();
		}
		// *************************************
		myAgent.send(message);
	}

	/**
	 * Erzeugt eine TARGET_FOUND INFORM Message mit den Zieldaten im Content und
	 * versendet sie an die angegebenen Empfänger
	 * 
	 * @param receivers
	 *            Die Empfaenger der Nachricht.
	 * @param result
	 *            <tt>TargetSearchResult</tt> Objekt mit den Koordinaten des
	 *            Zielobjekts
	 * 
	 * @throws IllegalArgumentException
	 *             Falls <tt>receivers</tt> nicht mindestens einen Eintrag hat.
	 * @throws NullPointerException
	 *             Falls <tt>receivers</tt> <tt>null</tt> ist.
	 */
	protected void buildAndSendTargetFoundMessage(AID[] receivers, TargetSearchResult result) {
		ArgumentCheck.notNull(receivers);
		ArgumentCheck.isTrue(receivers.length > 0);

		ACLMessage message = new ACLMessage(ACLMessage.INFORM);
		for (AID currentReceiver : receivers) {
			message.addReceiver(currentReceiver);
		}
		message.setConversationId(MASEvents.TARGET_FOUND.getName());

		// *************************************
		// ontologie relevantes zeuch
		BaseTestAgent bta = (BaseTestAgent) myAgent;
		// Content Language und Ontologie der Message setzen:
		message.setLanguage(bta.getCodec().getName());
		message.setOntology(bta.getOntology().getName());

		TargetData data = new TargetData(result);

		try {
			// JADE konvertiert das SearchableObjectData JavaObjekt in einen SL
			// language String:
			myAgent.getContentManager().fillContent(message, data);
		} catch (CodecException ce) {
			ce.printStackTrace();
		} catch (OntologyException oe) {
			oe.printStackTrace();
		}
		// *************************************
		myAgent.send(message);
	}

	/**
	 * Erzeugt eine ENVIRONMENT_SCAN_FINISHED INFORM Message mit den
	 * Umgebungsdaten im Content und versendet sie an die angegebenen Empfänger
	 * 
	 * @param receivers
	 *            Die Empfaenger der Nachricht.
	 * @param result
	 *            <tt>EnvironmentScanResult</tt> Objekt mit den Ergebnissen der
	 *            Umgebungssuche
	 * 
	 * @throws IllegalArgumentException
	 *             Falls <tt>receivers</tt> nicht mindestens einen Eintrag hat.
	 * @throws NullPointerException
	 *             Falls <tt>receivers</tt> <tt>null</tt> ist.
	 */
	protected void buildAndSendEnvironmentScanFinishedMessage(AID[] receivers, EnvironmentScanResult result) {
		ArgumentCheck.notNull(receivers);
		ArgumentCheck.isTrue(receivers.length > 0);

		ACLMessage message = new ACLMessage(ACLMessage.INFORM);
		for (AID currentReceiver : receivers) {
			message.addReceiver(currentReceiver);
		}
		message.setConversationId(MASEvents.ENVIRONMENT_SCAN_FINISHED.getName());

		// *************************************
		// ontologie relevantes zeuch
		BaseTestAgent bta = (BaseTestAgent) myAgent;
		// Content Language und Ontologie der Message setzen:
		message.setLanguage(bta.getCodec().getName());
		message.setOntology(bta.getOntology().getName());

		EnvironmentData data = new EnvironmentData(result);

		try {
			// JADE konvertiert das SearchableObjectData JavaObjekt in einen SL
			// language String:
			myAgent.getContentManager().fillContent(message, data);
		} catch (CodecException ce) {
			ce.printStackTrace();
		} catch (OntologyException oe) {
			oe.printStackTrace();
		}
		// *************************************
		myAgent.send(message);
	}

	/**
	 * Wartet die angegebene Zahl Millisekunden. Dabei wird der komplette Agent
	 * angehalten.
	 * 
	 * @param millisToWait
	 *            Wie viele Millisekunden gewartet werden soll.
	 */
	protected void doWait(long millisToWait) {
		try {
			Thread.sleep(millisToWait);
		} catch (InterruptedException e) {
			((BaseTestAgent) myAgent).log(Level.WARNING, myAgent.getName() + ": thread interrupted", e);
		}
	}

	/**
	 * Sendet eine <tt>INFORM</tt> Nachricht an den zu testenden Agenten mit der
	 * Conversation-ID des gegebenen Events.
	 * 
	 * @param event
	 *            Das zu sendende <tt>INFORM</tt> bezieht sich auf dieses Event.
	 * 
	 * @throws NullPointerException
	 *             Falls <tt>event</tt> <tt>null</tt> ist.
	 */
	protected void buildAndSendInformMessageToTestedAgent(MASEvents event) {
		ArgumentCheck.notNull(event);

		buildAndSendMessage(null, event.getName(), ACLMessage.INFORM, new AID[] { ((BaseTestAgent) myAgent).getAgentToTest() });
	}

}
